package model;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.GLUT;

import java.io.IOException;
import java.nio.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * Project2.java <BR>
 * author: Andrew Gormley, Kyle Hill
 *
 */
public class Project2 implements ActionListener {

    private static Animator animator;
    private static JPanel controls;

    public static void main(String[] args) {

	JFrame frame = new JFrame("Project 2");
	frame.getContentPane().setLayout(new BorderLayout());
	controls = new JPanel(new GridLayout());
	GLCapabilities caps = new GLCapabilities();
	caps.setAlphaBits(8);
	final GLJPanel drawable = new GLJPanel(caps);
	drawable.setOpaque(false);
	drawable.addGLEventListener(new Renderer());

	GLJPanel gradientPanel = new GLJPanel() {

	    public void paintComponent(Graphics g) {
		((Graphics2D) g).setPaint(new GradientPaint(0, 0, Color.WHITE,
			getWidth(), getHeight(), Color.DARK_GRAY));
		g.fillRect(0, 0, getWidth(), getHeight());
	    }
	};
	gradientPanel.setLayout(new BorderLayout());
	frame.getContentPane().add(gradientPanel, BorderLayout.CENTER);

	gradientPanel.add(drawable, BorderLayout.CENTER);

	final JCheckBox transparentCheckBox = new JCheckBox("Transparent", true);
	transparentCheckBox.addActionListener(new ActionListener() {

	    public void actionPerformed(ActionEvent e) {
		drawable.setOpaque(!transparentCheckBox.isSelected());
	    }
	});
	final JCheckBox animationCheckBox = new JCheckBox("Animation", false);
	animationCheckBox.addActionListener(new Project2());
	controls.add(transparentCheckBox);
	controls.add(animationCheckBox);
	frame.getContentPane().add(controls, BorderLayout.SOUTH);


	frame.setSize(800, 800);
	animator = new Animator(drawable);
	frame.addWindowListener(new WindowAdapter() {

	    public void windowClosing(WindowEvent e) {
		// Run this on another thread than the AWT event queue to
		// make sure the call to Animator.stop() completes before
		// exiting
		new Thread(new Runnable() {

		    public void run() {
			if (animator.isAnimating()) {
			    animator.stop();
			}
			System.exit(0);
		    }
		}).start();
	    }
	});
	frame.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
	if (e.getActionCommand().equals("Animation") && animator.isAnimating()) {
	    animator.stop();
	} else if (e.getActionCommand().equals("Animation")) {

	    animator.start();

	}
    }

    static class Renderer implements GLEventListener, MouseListener, MouseMotionListener {

	private float view_rotx = 0.0f, view_roty = 0.0f, view_rotz = 0.0f;
	private int head, mouth, nose;
	private float angle = 0.0f;
	private float xCirc = 0f;
	private float yCirc = 0f;
	private float talk = -.2f;
	private int prevMouseX, prevMouseY;
	private boolean mouseRButtonDown = false;
	private boolean up;
	private int eye;
	private int pupil;
	private int jaw;
	private int body;
	private int thigh;
	private int calf;
	private int arm;
	private int knee, feet;
	private float walk = 0f;
	private boolean stepUp;
	private float circleAngle = 0f;
	private int texture;
	private int tex;
	private int shoulder;
	private int hand;

	public void init(GLAutoDrawable drawable) {
	    // Use debug pipeline
	    // drawable.setGL(new DebugGL(drawable.getGL()));

	    GL gl = drawable.getGL();
	    // System.err.println("INIT GL IS: " + gl.getClass().getName());

	    float pos[] = {5.0f, 5.0f, 10f, 0.0f};
	    float red[] = {0.8f, 0.1f, 0.0f, 1.0f};
	    float green[] = {0.0f, 0.8f, 0.2f, 1.0f};
	    float blue[] = {0.2f, 0.2f, 1.0f, 1.0f};
	    float orange[] = {1.0f, 0.5f, 0.0f, 1.0f};
	    float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
	    float black[] = {0f, 0f, 0f, 1f};

	    gl.glClearColor(0f, 0f, 0f, 1f);
	    gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, FloatBuffer.wrap(pos));
	    gl.glEnable(GL.GL_CULL_FACE);
	    gl.glEnable(GL.GL_LIGHTING);
	    gl.glEnable(GL.GL_LIGHT0);
	    gl.glEnable(GL.GL_DEPTH_TEST);

	    /* set projection parameters */
	    float h = (float) 100 / (float) 300;

	    gl.glMatrixMode(GL.GL_PROJECTION);
	    gl.glFrustum(-1.5f, 1.5f, -1.5, 1.5, 5.0f, 100.0f);
	    gl.glMatrixMode(GL.GL_MODELVIEW);
	    gl.glLoadIdentity();
	    gl.glTranslatef(0.0f, .0f, -80.0f);


	    head = gl.glGenLists(1);
	    gl.glNewList(head, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    head(gl);
	    gl.glEndList();

	    hand = gl.glGenLists(1);
	    gl.glNewList(hand, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    hand(gl);
	    gl.glEndList();
	    
	    body = gl.glGenLists(1);
	    gl.glNewList(body, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    body(gl);
	    gl.glEndList();

	    mouth = gl.glGenLists(1);
	    gl.glNewList(mouth, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(black));
	    mouth(gl);
	    gl.glEndList();

	    jaw = gl.glGenLists(1);
	    gl.glNewList(jaw, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    jaw(gl);
	    gl.glEndList();

	    nose = gl.glGenLists(1);
	    gl.glNewList(nose, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(orange));
	    nose(gl);
	    gl.glEndList();

	    eye = gl.glGenLists(1);
	    gl.glNewList(eye, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(white));
	    eye(gl);
	    gl.glEndList();

	    pupil = gl.glGenLists(1);
	    gl.glNewList(pupil, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(black));
	    pupil(gl);
	    gl.glEndList();

	    thigh = gl.glGenLists(1);
	    gl.glNewList(thigh, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    thigh(gl);
	    gl.glEndList();

	    arm = gl.glGenLists(1);
	    gl.glNewList(arm, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    arm(gl);
	    gl.glEndList();

	    calf = gl.glGenLists(1);
	    gl.glNewList(calf, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    calf(gl);
	    gl.glEndList();

	    knee = gl.glGenLists(1);
	    gl.glNewList(knee, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    knee(gl);
	    gl.glEndList();

	    shoulder = gl.glGenLists(1);
	    gl.glNewList(shoulder, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    knee(gl);
	    gl.glEndList();
	    
	    feet = gl.glGenLists(1);
	    gl.glNewList(feet, GL.GL_COMPILE);
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, FloatBuffer.wrap(red));
	    feet(gl);
	    gl.glEndList();

	    tex = gl.glGenLists(1);
	    gl.glNewList(tex, GL.GL_COMPILE);
	    gl.glEnable(GL.GL_NORMALIZE);
	    texture = genTexture(gl);
	    gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
	    TextureReader.Texture texture = null;

	    try {
		texture = TextureReader.readTexture("images/elmobackground2.bmp");
	    } catch (IOException e) {
		e.printStackTrace();
		throw new RuntimeException(e);
	    }
	    float[] noMat = {1.0f, 1.0f, 1.0f, .7f};
	    gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, noMat, 0);
	    GLU glu = new GLU();
	    makeRGBTexture(gl, glu, texture, GL.GL_TEXTURE_2D, true);
	    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
	    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);

	    gl.glEnable(GL.GL_TEXTURE_2D);

	    gl.glEndList();

	    drawable.addMouseListener(this);
	    drawable.addMouseMotionListener(this);
	}

	public void display(GLAutoDrawable drawable) {
	    angle += 2.0f;
	    circleAngle += 1f;

	    if (talk >= -.2f) {
		up = false;
	    } else if (talk <= -0.5f) {
		up = true;
	    }
	    if (up) {
		talk = talk + 0.01f;
	    } else {
		talk = talk - 0.01f;
	    }

	    if (walk >= 0f) {
		stepUp = false;
	    } else if (walk <= -5f) {
		stepUp = true;
	    }
	    if (up) {
		walk = walk + 0.01f;
	    } else {
		walk = walk - 0.01f;
	    }


	    GL gl = drawable.getGL();
	    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

	    // walk in a circle
	    gl.glPushMatrix();
	    gl.glTranslatef(10 * (float) Math.cos(circleAngle * .01), 0f, 10 * (float) Math.sin(circleAngle * .01));
	    gl.glRotatef(-circleAngle * .573f, 0f, 1f, 0f);


	    gl.glPushMatrix();
	    //gl.glTranslatef(0.0f, 0.1f, 0.0f);
	    //gl.glRotatef(angle, 0.1f, 0.0f, 0.0f);
	    gl.glScalef(1.2f, 1f, 1f);
	    gl.glCallList(head);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(0.0f, -.5f, .2f);
	    gl.glScalef(1.19f, .8f, 1f);
	    gl.glCallList(mouth);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(0.0f, talk / 2 - .5f, talk + .5f);
	    gl.glScalef(1.19f, .8f, 1f);
	    gl.glCallList(jaw);
	    gl.glPopMatrix();


	    gl.glPushMatrix();
	    gl.glTranslatef(0.0f, 0.5f, 1.2f);
	    gl.glScalef(.7f, 1.2f, 1.0f);
	    gl.glCallList(nose);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(.7f, 2f, 1f);
	    //gl.glScalef(.7f, 1.2f, 1.0f);
	    gl.glCallList(eye);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(-0.7f, 2f, 1f);
	    //gl.glScalef(.7f, 1.2f, 1.0f);
	    gl.glCallList(eye);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(.6f, 2f, 1.5f);
	    //gl.glScalef(.7f, 1.2f, 1.0f);
	    gl.glCallList(pupil);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(-0.6f, 2f, 1.5f);
	    //gl.glScalef(.7f, 1.2f, 1.0f);
	    gl.glCallList(pupil);
	    gl.glPopMatrix();

	    //	gl.glPopMatrix();   //pop headbob

	    gl.glPushMatrix();
	    gl.glTranslatef(0.0f, -4.9f, -.3f);
	    gl.glScalef(.6f, .9f, .4f);
	    gl.glCallList(body);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(2.0f, -3.0f, 0.0f);
	    gl.glCallList(shoulder);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(-2.0f, -3.0f, 0.0f);
	    gl.glCallList(shoulder);
	    gl.glPopMatrix();
	    
	    gl.glPushMatrix();
	    gl.glTranslatef(2.0f, -3.0f, 0.0f);
	    gl.glRotatef(60, 0.0f, 0.0f, -1.0f);
	    if (up) {
		gl.glRotatef((float) -Math.sin(walk * 10) * 100, 1.0f, 1.0f, 0.0f);
	    } else {
		gl.glRotatef((float) Math.sin(walk * 10) * 100, -1.0f, 1.0f, 0.0f);
	    }
	    gl.glCallList(arm);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    //make arm face other way then wiggle
	    gl.glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
	    gl.glTranslatef(2.0f, 3.0f, 0.0f);
	    gl.glRotatef(60.0f, 0.0f, 0.0f, 1.0f);
	    if (up) {
		gl.glRotatef((float) Math.sin(walk * 10) * 100, 1.0f, 1.0f, 0.0f);
	    } else {
		gl.glRotatef((float) -Math.sin(walk * 10) * 100, 1.0f, 1.0f, 0.0f);
	    }
	    gl.glCallList(arm);
	    gl.glPopMatrix();


	    gl.glPushMatrix();
	    gl.glTranslatef(1.0f, -7.5f, -.5f);
	    gl.glRotatef(walk * 100, 1f, 0f, 0f);
	    gl.glCallList(thigh);
	    gl.glPopMatrix();


	    gl.glPushMatrix();
	    gl.glTranslatef(-1.0f, -7.5f, -.5f);
	    gl.glRotatef(-walk * 100 - 30, 1f, 0f, 0f);
	    gl.glCallList(thigh);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(1.0f, -7.5f - 3.5f * (float) Math.cos(walk * 2), -.5f - 3.5f * (float) Math.sin(walk * 2));
	    //gl.glRotatef(0, 1f, 0f, 0f);
	    gl.glCallList(calf);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(-1.0f, -13.9f + 3.5f * (float) Math.cos(-walk * 2), 1.5f - 3.5f * (float) Math.sin(-walk * 2));
	    //gl.glRotatef(-walk * 100 - 30, 1f, 0f, 0f);
	    gl.glCallList(calf);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(1.0f, -7.5f - 3.5f * (float) Math.cos(walk * 2), -.5f - 3.5f * (float) Math.sin(walk * 2));
	    //gl.glRotatef(0, 1f, 0f, 0f);
	    gl.glCallList(knee);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(-1.0f, -13.9f + 3.5f * (float) Math.cos(-walk * 2), 1.5f - 3.5f * (float) Math.sin(-walk * 2));
	    //gl.glRotatef(-walk * 100 - 30, 1f, 0f, 0f);
	    gl.glCallList(knee);
	    gl.glPopMatrix();
	    

	    

	    gl.glPushMatrix();
	    gl.glTranslatef(1.0f, -10.5f - 3.5f * (float) Math.cos(walk * 2), -.3f - 3.5f * (float) Math.sin(walk * 2));
	    //gl.glRotatef(0, 1f, 0f, 0f);
	    gl.glCallList(feet);
	    gl.glPopMatrix();

	    gl.glPushMatrix();
	    gl.glTranslatef(-1.0f, -16.9f + 3.5f * (float) Math.cos(-walk * 2), 1.7f - 3.5f * (float) Math.sin(-walk * 2));
	    //gl.glRotatef(-walk * 100 - 30, 1f, 0f, 0f);
	    gl.glCallList(feet);
	    gl.glPopMatrix();

	    gl.glPopMatrix();


	    gl.glPushMatrix();

	    gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
	    gl.glCallList(tex);
	    gl.glDisable(GL.GL_CULL_FACE);
	    gl.glDisable(GL.GL_LIGHTING);
	    gl.glDisable(GL.GL_LIGHT0);
	    //gl.glDisable(GL.GL_DEPTH_TEST);

	    gl.glBegin(GL.GL_QUADS);
	    gl.glTexCoord2f(0.0f, 0.0f);
	    gl.glVertex3f(-30.0f, -30.0f, -15.0f);
	    gl.glTexCoord2f(1.0f, 0.0f);
	    gl.glVertex3f(30.0f, -30.0f, -15.0f);
	    gl.glTexCoord2f(1.0f, 1.0f);
	    gl.glVertex3f(30.0f, 30.0f, -15.0f);
	    gl.glTexCoord2f(0.0f, 1.0f);
	    gl.glVertex3f(-30.0f, 30.0f, -15.0f);
	    gl.glEnd();
	    gl.glEnable(GL.GL_CULL_FACE);
	    gl.glEnable(GL.GL_LIGHTING);
	    gl.glEnable(GL.GL_LIGHT0);
	    //	gl.glEnable(GL.GL_DEPTH_TEST);

	    gl.glPopMatrix();
	}

	//x = cx + r * cos(a)
	//y = cy + r * sin(a)
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
	}
	
	private void hand(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glTranslatef(0.0f, 0.10f, 0.0f);
	    glut.glutSolidSphere(.75, 30, 15);   // radius 0.75, 8 longitude lines,
	    gl.glScalef(1f, .8f, .8f);

	}

	private void head(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glTranslatef(0.0f, 0.10f, 0.0f);
	    glut.glutSolidSphere(2.0, 30, 15);   // radius 0.75, 8 longitude lines,

	}

	private void arm(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
	    glut.glutSolidCylinder(.4, 3.5, 15, 15);
	}

	private void thigh(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
	    glut.glutSolidCylinder(.4, 3.5, 15, 15);

	}

	private void calf(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
	    glut.glutSolidCylinder(.4, 3.5, 15, 10);
	}

	private void jaw(GL gl) {
	    GLUT glut = new GLUT();

	    glut.glutSolidSphere(1.8, 30, 15);

	}

	private void eye(GL gl) {
	    GLUT glut = new GLUT();
	    glut.glutSolidSphere(0.7, 20, 10);
	}

	private void pupil(GL gl) {
	    GLUT glut = new GLUT();
	    glut.glutSolidSphere(0.27, 20, 10);

	}

	private void nose(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glTranslatef(0.0f, 0.20f, 0.0f);
	    //
	    glut.glutSolidSphere(1.0, 18, 6);

	}

	private void mouth(GL gl) {
	    GLUT glut = new GLUT();

	    glut.glutSolidSphere(1.8, 30, 15);
	}

	private void knee(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glScalef(1f, 1f, 1f);
	    glut.glutSolidSphere(.5, 18, 15);
	}

	private void body(GL gl) {
	    GLUT glut = new GLUT();
	    glut.glutSolidSphere(4, 18, 15);

	}

	private void feet(GL gl) {
	    GLUT glut = new GLUT();
	    gl.glScalef(1f, .6f, 1.4f);
	    glut.glutSolidSphere(1, 18, 15);
	}

	// Methods required for the implementation of MouseListener
	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
	    prevMouseX = e.getX();
	    prevMouseY = e.getY();
	    if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
		mouseRButtonDown = true;
	    }
	}

	public void mouseReleased(MouseEvent e) {
	    if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
		mouseRButtonDown = false;
	    }
	}

	public void mouseClicked(MouseEvent e) {
	}

	// Methods required for the implementation of MouseMotionListener
	public void mouseDragged(MouseEvent e) {
	    int x = e.getX();
	    int y = e.getY();
	    Dimension size = e.getComponent().getSize();

	    float thetaY = 360.0f * ((float) (x - prevMouseX) / (float) size.width);
	    float thetaX = 360.0f * ((float) (prevMouseY - y) / (float) size.height);

	    prevMouseX = x;
	    prevMouseY = y;

	    view_rotx += thetaX;
	    view_roty += thetaY;
	}

	public void mouseMoved(MouseEvent e) {
	}

	private void makeRGBTexture(GL gl, GLU glu, TextureReader.Texture img, int target, boolean mipmapped) {
	    if (mipmapped) {
		glu.gluBuild2DMipmaps(target, GL.GL_RGB8, img.getWidth(), img.getHeight(), GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img.getPixels());
	    } else {
		gl.glTexImage2D(target, 0, GL.GL_RGB, img.getWidth(), img.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img.getPixels());
	    }
	}

	private int genTexture(GL gl) {
	    final int[] tmp = new int[1];
	    gl.glGenTextures(1, tmp, 0);
	    return tmp[0];
	}
    }
}

